﻿using System;
using System.Windows;
using System.Windows.Input;

namespace Purdue.Helper
{
    /// <summary>
    /// Simple helper class for dragging visuals with the mouse. Handles all the mouse events and measurements.
    /// </summary>
    public class DragHelper
    {
        /// <summary>
        /// The mouse position of the last MouseDown event
        /// </summary>
        Point initialMousePosition;
        /// <summary>
        /// The mouse position of the last MouseMove event
        /// </summary>
        Point lastMousePosition;
        /// <summary>
        /// The element that we should control
        /// </summary>
        FrameworkElement targetElement;

        public DragHelper(FrameworkElement targetElement)
        {
            if (targetElement == null)
                throw new ArgumentNullException("targetElement");

            this.targetElement = targetElement;

            targetElement.MouseLeftButtonDown += targetElement_MouseLeftButtonDown;
            targetElement.MouseLeftButtonUp += targetElement_MouseLeftButtonUp;
            targetElement.MouseMove += targetElement_MouseMove;
            targetElement.Unloaded += targetElement_Unloaded;
        }

        /// <summary>
        /// The user just started dragging by clicking into the target element.
        /// </summary>
        public event EventHandler BeginDrag;
        /// <summary>
        /// The user has dragged the target.
        /// </summary>
        public event EventHandler<DragHelperEventArgs> Dragged;
        /// <summary>
        /// The user finished dragging by releasing the mouse button.
        /// </summary>
        public event EventHandler EndDrag;


        private void targetElement_Unloaded(object sender, RoutedEventArgs e)
        {
            targetElement.MouseLeftButtonDown -= targetElement_MouseLeftButtonDown;
            targetElement.MouseLeftButtonUp -= targetElement_MouseLeftButtonUp;
            targetElement.MouseMove -= targetElement_MouseMove;
            targetElement.Unloaded -= targetElement_Unloaded;
            targetElement = null;
        }

        private void targetElement_MouseMove(object sender, MouseEventArgs e)
        {
            if (Mouse.Captured == targetElement)
            {
                Point currentPosition = e.GetPosition(targetElement);

                if (lastMousePosition != currentPosition)
                {
                    if (Dragged != null)
                    {
                        DragHelperEventArgs dragEventArgs = new DragHelperEventArgs
                        {
                            LastMove = currentPosition - lastMousePosition,
                            TotalMove = currentPosition - initialMousePosition,
                            MouseEventArgs = e,
                            TargetElement = targetElement
                        };

                        Dragged(this, dragEventArgs);
                    }

                    lastMousePosition = currentPosition;
                }
            }
        }

        private void targetElement_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (Mouse.Captured == targetElement)
            {
                targetElement.ReleaseMouseCapture();

                if (EndDrag != null)
                {
                    EndDrag(this, EventArgs.Empty);
                }
            }
        }

        private void targetElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            lastMousePosition = initialMousePosition = e.GetPosition(targetElement);

            if (targetElement.CaptureMouse())
            {
                if (BeginDrag != null)
                {
                    BeginDrag(this, EventArgs.Empty);
                }
            }
        }
    }

    public class DragHelperEventArgs : EventArgs
    {
        /// <summary>
        /// The amount the cursor was moved since the last drag position.
        /// </summary>
        public Vector LastMove { get; set; }
        /// <summary>
        /// The amount the cursor was moved relative to the mouse down positon.
        /// </summary>
        public Vector TotalMove { get; set; }
        /// <summary>
        /// The event args of the MouseMove event that triggered this event.
        /// </summary>
        public MouseEventArgs MouseEventArgs { get; set; }
        /// <summary>
        /// The target element.
        /// </summary>
        public FrameworkElement TargetElement { get; set; }
    }
}
